/*
 * vim:set spell spl=en fo=wan1croqlt tw=80 ts=2 sw=2 sts=2 sta et ai cin fenc=utf-8 ff=unix:
 *
 * Copyright (C) 2008, 2009 Mihai Şucan
 *
 * This file is part of libmacrame.
 *
 * Libmacrame is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Libmacrame is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Libmacrame.  If not, see <http://www.gnu.org/licenses/>.
 *
 * $URL: http://code.google.com/p/libmacrame $
 * $Date: 2009-04-17 18:32:39 +0300 $
 *
 */

/**
 * @author <a lang="ro" href="http://www.robodesign.ro/mihai">Mihai Şucan</a>
 * @version pre-alpha
 * @requires core.js
 *
 * @fileOverview This is a plugin for libmacrame which adds important methods 
 * for manipulating strings, arrays and objects.
 */

(function () {
// We will use $ to refer to libmacrame in this plugin.
var $ = libmacrame;

/*
 * If extend_objects is set to true:
 * - adds several static methods to the global Object.
 * - adds new methods to the Array.prototype.
 * - adds new methods to the String.prototype.
 */
var extend_objects = true;

// We store all the methods in $.js for easy reuse in other cases.

/**
 * Holds methods for manipulating several types of JavaScript objects.
 *
 * <p>This plugin extends the global Object, Array and String prototypes, adding 
 * new methods.
 *
 * <p>The new methods implement common functionality.
 *
 * @namespace Holds methods for manipulating several types of JavaScript 
 * objects.
 */
$.js = {};

/**
 * Holds the methods used for manipulating objects. These methods are very 
 * similar to their Array counter-parts.
 *
 * <p>By default, the global <var>Object</var> (not the 
 * <var>Object.prototype</var>!) is extended to contain all the methods in this 
 * namespace. Besides these methods, <var>Object</var> will have one more 
 * method: <code>Object.extend</code>, which is an alias of {@link $.extend}.
 *
 * <p>All of these methods use their <var>this</var> object. As such, you need 
 * to pass the correct <var>this</var> object.
 *
 * <p>Note that the methods provided iterate over the object properties using 
 * <code>for (<var>property</var> in <var>this</var>)</code>. As such, the order 
 * depends on the browser implementation. Also, those properties which are not 
 * enumerable 
 * (<code><var>this</var>.propertyIsEnumerable(<var>property</var>)</code>) are 
 * skipped.
 *
 * <p>In the examples provided for each method we will assume that the objects 
 * defined are already extended. Thus code like 
 * <code><var>obj</var>.filter(<var>callback</var>)</code> can be written 
 * directly.
 *
 * @example
 * // To use these methods proceed as follows:
 *
 * <code>var <var>obj</var> = {'a' : 'test1', 'b' : 'test2', 'c' : 'test3'};
 *
 * Object.forEach.call(<var>obj</var>, function (<var>value</var>) {
 *   // ...
 * });</code>
 *
 * // Instead of forEach, you can use any of the methods defined in this
 * // namespace.
 *
 * @example
 * Additionally, you can extend your object to have all these methods:
 *
 * <code>Object.extend.call(<var>obj</var>, Object);</code>
 *
 * // After doing so, you can directly use any desired method.
 * // For example:
 * <code><var>obj</var>.forEach(<var>callback</var>)</code>.
 *
 * @namespace Holds the methods used for manipulating Objects.
 *
 * @see $.js.Array The Array functions which provide similar functionality.
 */
$.js.Object = {
  /**
   * Filter the current object using the <var>callback</var> function.
   *
   * <p>The <var>callback</var> function is invoked for each property in the 
   * current object, and it is given three arguments: the value, the property 
   * name, and the entire object being iterated over.
   *
   * <p>This method does not alter the current object.
   *
   * @example
   * <code>var <var>obj1</var> = {'a' : 5, 'b' : 11, 'c' : 7};</code>
   *
   * // Filter only the properties with a value lower than 10:
   *
   * <code>var <var>obj2</var> = <var>obj1</var>.filter(
   *   function (<var>value</var>, <var>property</var>, <var>object</var>) {
   *     return <var>value</var> &lt; 10;
   *   }
   * );</code>
   *
   * // Now <code><var>obj2</var> = {'a' : 5, 'c' : 7}</code>.
   *
   * @param {Function} callback The <var>callback</var> function used for 
   * filtering the current object.
   *
   * @param {Object} [thisObject] The object which will be the <var>this</var> 
   * within the scope of the <var>callback</var> function, for each invocation.
   *
   * @throws {TypeError} If <var>callback</var> is not a function.
   *
   * @returns {Object} The new object contains only the properties for which the 
   * <var>callback</var> function returned true.
   *
   * @see $.js.Array.filter
   */
  filter : function (callback, thisObject) {
    if (typeof callback != 'function') {
      throw new TypeError('The first argument must be a function.');
    }

    var resObj = {}, name;
    for (name in this) {
      if (this.propertyIsEnumerable(name) && callback.call(thisObject, this[name], 
          name, this)) {
        resObj[name] = this[name];
      }
    }

    return resObj;
  },

  /**
   * Execute the <var>callback</var> function for each property in the current 
   * object.
   *
   * <p>The <var>callback</var> function is invoked with three arguments: the 
   * value, the property name, and the entire object being iterated over.
   *
   * <p>This method does not alter the current object.
   *
   * @example
   * <code>var <var>obj</var> = {'a' : 10, 'b' : 11, 'c' : 12};</code>
   *
   * // alert() each property and value.
   * <code><var>obj</var>.forEach(
   *   function (<var>value</var>, <var>property</var>) {
   *     alert(<var>property</var> + ' : ' + <var>value</var>);
   *   }
   * );</code>
   *
   * @param {Function} callback The <var>callback</var> function used for 
   * traversing the current object.
   *
   * @param {Object} [thisObject] The object which will be the <var>this</var> 
   * within the scope of the <var>callback</var> function, for each invocation.
   *
   * @throws {TypeError} If <var>callback</var> is not a function.
   *
   * @see $.js.Array.forEach
   */
  forEach : function (callback, thisObject) {
    if (typeof callback != 'function') {
      throw new TypeError('The first argument must be a function.');
    }

    for (var name in this) {
      if (this.propertyIsEnumerable(name)) {
        callback.call(thisObject, this[name], name, this);
      }
    }
  },

  /**
   * Check if the <var>callback</var> function returns true for every property 
   * in the current object.
   *
   * <p>The <var>callback</var> function is invoked for each property in the 
   * current object, and it is given three arguments: the value, the property 
   * name, and the entire object being iterated over.
   *
   * <p>Iteration over the object stops once reaching the end, or once the 
   * <var>callback</var> function returns false.
   *
   * <p>This method does not alter the current object.
   *
   * @example
   * <code>var <var>obj</var> = {'a' : 10, 'b' : 11, 'c' : 12};</code>
   *
   * // The following returns true.
   * <code><var>obj</var>.every(function (<var>value</var>) {
   *   return <var>value</var> &gt; 9;
   * });</code>
   *
   * @param {Function} callback The <var>callback</var> function used for 
   * traversing the current object.
   *
   * @param {Object} [thisObject] The object which will be the <var>this</var> 
   * within the scope of the <var>callback</var> function, for each invocation.
   *
   * @returns {Boolean} False is returned if the <var>callback</var> returns 
   * false once. Otherwise, this method returns true.
   *
   * @throws {TypeError} If <var>callback</var> is not a function.
   *
   * @see $.js.Array.every
   */
  every : function (callback, thisObject) {
    if (typeof callback != 'function') {
      throw new TypeError('The first argument must be a function.');
    }

    for (var name in this) {
      if (this.propertyIsEnumerable(name) && !callback.call(thisObject, 
            this[name], name, this)) {
        return false;
      }
    }

    return true;
  },

  /**
   * Check if the <var>callback</var> function returns true for at least one 
   * property in the current object.
   *
   * <p>The <var>callback</var> function is invoked for each property in the 
   * current object, and it is given three arguments: the value, the property 
   * name, and the entire object being iterated over.
   *
   * <p>Iteration over the object stops once reaching the end, or once the 
   * <var>callback</var> function returns true.
   *
   * <p>This method does not alter the current object.
   *
   * @example
   * <code>var <var>obj</var> = {'a' : 10, 'b' : 11, 'c' : 12};</code>
   *
   * // The following returns true.
   * <code><var>obj</var>.some(function (<var>value</var>) {
   *   return <var>value</var> &gt; 11;
   * });</code>
   *
   * @param {Function} callback The <var>callback</var> function used for 
   * traversing the current object.
   *
   * @param {Object} [thisObject] The object which will be the <var>this</var> 
   * within the scope of the <var>callback</var> function, for each invocation.
   *
   * @throws {TypeError} If <var>callback</var> is not a function.
   *
   * @returns {Boolean} True is returned if the <var>callback</var> returns true 
   * once. Otherwise, this method returns false.
   *
   * @see $.js.Array.some
   */
  some : function (callback, thisObject) {
    if (typeof callback != 'function') {
      throw new TypeError('The first argument must be a function.');
    }

    for (var name in this) {
      if (this.propertyIsEnumerable(name) && callback.call(thisObject, this[name], 
            name, this)) {
        return true;
      }
    }

    return false;
  },

  /**
   * Create a new object with the same properties as the current object using 
   * the values returned by the <var>callback</var> function.
   *
   * <p>The <var>callback</var> function is invoked for each property in the 
   * current object, and it is given three arguments: the value, the property 
   * name, and the entire object being iterated over.
   *
   * <p>This method does not alter the current object.
   *
   * @example
   * <code>var <var>obj1</var> = {'a' : 5, 'b' : 11, 'c' : 7};</code>
   *
   * // Let's double the numbers:
   * <code>var <var>obj2</var> = <var>obj1</var>.map(function (<var>value</var>) {
   *   return <var>value</var> * 2;
   * });</code>
   *
   * // Now <code><var>obj2</var> = {'a' : 10, 'b' : 22, 'c' : 14}</code>.
   *
   * @param {Function} callback The <var>callback</var> function used for 
   * traversing the current object.
   *
   * @param {Object} [thisObject] The object which will be the <var>this</var> 
   * within the scope of the <var>callback</var> function, for each invocation.
   *
   * @throws {TypeError} If <var>callback</var> is not a function.
   *
   * @returns {Object} The new object has the same properties as the current 
   * object, but the values are those returned by the <var>callback</var> 
   * function.
   *
   * @see $.js.Array.map
   */
  map : function (callback, thisObject) {
    if (typeof callback != 'function') {
      throw new TypeError('The first argument must be a function.');
    }

    var resObj = {};

    for (var name in this) {
      if (this.propertyIsEnumerable(name)) {
        resObj[name] = callback.call(thisObject, this[name], name, this);
      }
    }

    return resObj;
  },

  /**
   * Apply the <var>callback</var> function to two values from the current 
   * object, simultaneously, for the purpose of reducing the object to a single 
   * value.
   *
   * <p>The <var>callback</var> function is called for each property in the 
   * current object, and it is given four arguments: the previous value, the 
   * current value, the current property name, and the entire object being 
   * iterated over.
   *
   * <p>This method does not alter the current object.
   *
   * <p>Note that the execution order is the same as doing <code>for (prop in 
   * obj)</code>.
   *
   * @example
   * <code>var <var>obj</var> = {'a' : 5, 'b' : 10, 'c' : 6};</code>
   *
   * // Let's calculate the sum:
   * <code>var <var>result</var> = <var>obj</var>.reduce(
   *   function (<var>sum</var>, <var>value</var>) {
   *     return <var>sum</var> + <var>value</var>;
   *   },
   * 0);</code>
   *
   * // Now <var>result</var> = 21.
   *
   * @param {Function} callback The <var>callback</var> function used for 
   * traversing the current object.
   *
   * @param [initialValue] The initial value used when the <var>callback</var> 
   * function is first invoked. If the initial value is not provided, then the 
   * method uses the value of the first property found in the object being 
   * iterated.
   *
   * @throws {TypeError} If <var>callback</var> is not a function.
   *
   * @returns The result of the last <var>callback</var> function invocation.
   *
   * @see $.js.Array.reduce
   * @see $.js.Array.reduceRight
   */
  reduce : function (callback, initialValue) {
    if (typeof callback != 'function') {
      throw new TypeError('The first argument must be a function.');
    }

    var res, found_initial = false;

    if (typeof initialValue != 'undefined') {
      found_initial = true;
      res = initialValue;
    }

    for (var name in this) {
      if (!this.propertyIsEnumerable(name)) {
        continue;
      }

      if (!found_initial) {
        found_initial = true;
        res = this[name];
        continue;
      }

      res = callback.call(null, res, this[name], name, this);
    }

    return res;
  }
};

/**
 * Holds the methods used for manipulating arrays and other array-like objects.
 * 
 * <p>These methods are very similar to their Object counter-parts. Also, most 
 * of the methods (if not all) are already implemented natively in the global 
 * <var>Array.prototype</var> object (new in JavaScript 1.6).
 *
 * <p>By default, the global <var>Array.prototype</var> object is extended to 
 * contain all the methods in this namespace, if they do not exists already.  
 * Thus, native Array methods are not overwritten.
 *
 * <p>All of these methods use their <var>this</var> object. As such, you need 
 * to pass the correct <var>this</var> object.
 *
 * <p>Note that the methods provided iterate over the object properties using 
 * <code>for (var <var>i</var> = 0; <var>i</var> &lt; <var>this</var>.length; 
 * <var>i</var>++) { ... <var>this[i]</var> ... }</code>. As such, any object 
 * can be used if it provides index-based access to its properties, together 
 * with the <var>length</var> property. 
 *
 * <p>The implementations do minimal type checks, such that the methods can be 
 * used together with NodeLists and other Array-like objects.
 *
 * <p>In the examples provided for each method we will assume that the arrays 
 * defined already have the methods. Thus code like 
 * <code><var>array</var>.filter(<var>callback</var>)</code> can be written 
 * directly.
 *
 * @example
 * // To use these methods with a NodeList proceed as follows:
 *
 * <code>var <var>nodes</var> = document.body.childNodes;
 *
 * $.js.Array.forEach.call(<var>nodes</var>, function (<var>node</var>) {
 *   // ...
 * });</code>
 *
 * // Instead of forEach, you can use any of the methods defined in this
 * // namespace.
 *
 * @example
 * Additionally, you can extend your object to have all these methods:
 *
 * <code>$.extend.call(NodeList, $.js.Array);</code>
 *
 * // After doing so, you can directly use any desired method.
 * // For example:
 * <code><var>nodes</var>.forEach(<var>callback</var>)</code>.
 *
 * @namespace Holds the methods used for manipulating arrays and other 
 * array-like objects.
 *
 * @see $.js.Object The Object methods which provide similar functionality for 
 * objects.
 */
$.js.Array = {
  /**
   * Extract a section of the current array and return it.
   *
   * @example
   * <code>var <var>arr1</var> = ['a', 'b', 'c', 'd'];
   * var <var>arr2</var> = <var>arr1</var>.slice(1, 3);</code>
   * // Now <code><var>arr2</var> = ['b', 'c']</code>.
   *
   * <code>var <var>arr3</var> = <var>arr1</var>.slice(1);</code>
   * // Now <code><var>arr3</var> = ['b', 'c', 'd']</code>.
   *
   * <code>var <var>arr4</var> = <var>arr1</var>.slice(0, -2);</code>
   * // Now <code><var>arr4</var> = ['a', 'b']</code>.
   *
   * @param {Number} begin Zero-based index from where to start the extraction 
   * of array elements.
   *
   * @param {Number} [end=this.length] Zero-based index where to end the 
   * extraction of array elements.
   * 
   * <p>If the argument is not given, then the end is considered the array 
   * length. Thus, the slice being extracted contains all the array elements 
   * from the given <var>begin</var> index.
   * 
   * <p>If the number is negative, then the <var>end</var> index is calculated 
   * by a simple formula: <code><var>end</var> = <var>array.length</var> 
   * - <var>end</var></code>. Thus, given a negative index like -3, it would 
   *   mean that the slice being extracted will not contain the last three 
   *   elements.
   *
   * @throws {TypeError} If <var>begin</var> is not a number, or if it is 
   * a negative number.
   *
   * @throws {TypeError} If <var>end</var> is provided, but it is not a number.
   *
   * @returns {Array} The new array returned contains all the elements starting 
   * from the <var>begin</var> index (including it) up to the <var>end</var> 
   * index (excluding it).
   */
  slice : function (begin, end) {
    if (isNaN(begin) || begin < 0) {
      throw new TypeError("The first argument is not a valid number.");
    } else if (end && isNaN(end)) {
      throw new TypeError("The second argument is not a valid number.");
    }

    var resArr = [], i = begin, n = this.length;

    if (end && end < 0) {
      end = n - end;
    }

    if (!end || end > n) {
      end = n;
    }

    for ( ; i < end; i++) {
      resArr.push(this[i]);
    }

    return resArr;
  },

  /**
   * Join all the elements from the current array into a single string using 
   * a separator.
   *
   * <p>Each element of the array is converted to a string using the automatic 
   * <code>toString()</code> method invocation. A string separator is added into 
   * the result string between each array element.
   *
   * <p>This method does not alter the current array.
   *
   * @example
   * <code>var <var>arr</var> = ['a', 'b', 'c'];
   * var <var>str</var> = <var>arr</var>.join('-');</code>
   *
   * // Now <code><var>str</var> = 'a-b-c'</code>.
   *
   * @param {String} [separator] The separator string which gets added between 
   * each array element into the result string. If the argument is not provided, 
   * then a comma will be used.
   *
   * @returns {String} The new string containing all the elements in the current 
   * array, converted into strings, separated by the given <var>separator</var>.
   */
  join : function (separator) {
    if (typeof separator == 'undefined') {
      separator = ',';
    }

    var res = '', i = 0, n = this.length;
    for ( ; i < n; i++) {
      if (i > 0) {
        res += separator;
      }
      res += this[i];
    }

    return res;
  },

  /**
   * Concatenate multiple arrays.
   *
   * <p>This method takes any number of arguments.
   *
   * @example
   * <code>var <var>arr1</var> = [5, 11, 7];
   * var <var>arr2</var> = <var>arr1</var>.concat('test', [1, 2, 3], 4);</code>
   *
   * // Now <code><var>arr2</var> = [5, 11, 7, 'test', 1, 2, 3, 4]</code>.
   *
   * @param value1 Array/value to concatenate.
   * @param [valueN] Array/value to concatenate.
   *
   * @returns {Array} The result is a new array consisting of:
   * <ol>
   *   <li>the <var>this</var> array object on which the <code>concat</code> 
   *   method was called;
   *   <li>each element of the arrays passed as arguments;
   *   <li>if an argument is not an array, then the argument value is added 
   *   as-is.
   * </ol>
   *
   * <p>The current array is not altered, nor the arrays passed as arguments.
   */
  concat : function () {
    var resArr = [], arr, y, i = -1, m, n = arguments.length;
    for ( ; i < n; i++) {
      if (i == -1) {
        arr = this;
      } else {
        arr = arguments[i];
      }

      if (!arr.length  || isNaN(arr.length)) {
        resArr.push(arrr);
        continue;
      }

      m = arr.length;
      for (y = 0; y < m; y++) {
        if (y in arr) {
          resArr.push(arr[y]);
        }
      }
    }

    return resArr;
  },

  /**
   * Filter the current array using the <var>callback</var> function.
   *
   * <p>The <var>callback</var> function is invoked for each element in the 
   * current array, and it is given three arguments: the value, the index, and 
   * the entire array being iterated over.
   *
   * <p>This method does not alter the current array.
   *
   * @example
   * <code>var <var>arr1</var> = [5, 11, 7];</code>
   *
   * // Filter only the elements with a value lower than 10:
   *
   * <code>var <var>arr2</var> = <var>arr1</var>.filter(
   *   function (<var>value</var>, <var>index</var>, <var>array</var>) {
   *     return <var>value</var> &lt; 10;
   *   }
   * );</code>
   *
   * // Now <code><var>arr2</var> = [5, 7]</code>.
   *
   * @param {Function} callback The <var>callback</var> function used for 
   * filtering the current array.
   *
   * @param {Object} [thisObject] The object which will be the <var>this</var> 
   * within the scope of the <var>callback</var> function, for each invocation.
   *
   * @throws {TypeError} If <var>callback</var> is not a function.
   *
   * @returns {Array} The new array contains only the elements for which the 
   * <var>callback</var> function returned true.
   *
   * @see $.js.Object.filter
   */
  filter : function (callback, thisObject) {
    if (typeof callback != 'function') {
      throw new TypeError('The first argument must be a function.');
    }

    var resArr = [], i = 0, n = this.length;
    for ( ; i < n; i++) {
      if ((i in this) && callback.call(thisObject, this[i], i, this)) {
        resArr.push(this[i]);
      }
    }

    return resArr;
  },

  /**
   * Execute the <var>callback</var> function for each element in the current 
   * array.
   *
   * <p>The <var>callback</var> function is invoked with three arguments: the 
   * value, the index, and the entire array being iterated over.
   *
   * <p>This method does not alter the current array.
   *
   * @example
   * <code>var <var>arr</var> = [10, 11, 12];</code>
   *
   * // alert() each index and value.
   * <code><var>arr</var>.forEach(
   *   function (<var>value</var>, <var>index</var>) {
   *     alert(<var>index</var> + ' : ' + <var>value</var>);
   *   }
   * );</code>
   *
   * @param {Function} callback The <var>callback</var> function used for 
   * traversing the current array.
   *
   * @param {Object} [thisObject] The object which will be the <var>this</var> 
   * within the scope of the <var>callback</var> function, for each invocation.
   *
   * @throws {TypeError} If <var>callback</var> is not a function.
   *
   * @see $.js.Object.forEach
   */
  forEach : function (callback, thisObject) {
    if (typeof callback != 'function') {
      throw new TypeError('The first argument must be a function.');
    }

    for (var i = 0, n = this.length, val ; i < n; i++) {
      if (i in this) {
        callback.call(thisObject, this[i], i, this);
      }
    }
  },

  /**
   * Check if the <var>callback</var> function returns true for every element in 
   * the current array.
   *
   * <p>The <var>callback</var> function is invoked for each element in the 
   * current array, and it is given three arguments: the value, the index, and 
   * the entire array being iterated over.
   *
   * <p>Iteration over the array stops once reaching the end, or once the 
   * <var>callback</var> function returns false.
   *
   * <p>This method does not alter the current array.
   *
   * @example
   * <code>var <var>arr</var> = [10, 11, 12];</code>
   *
   * // The following returns true.
   * <code><var>arr</var>.every(function (<var>value</var>) {
   *   return <var>value</var> &gt; 9;
   * });</code>
   *
   * @param {Function} callback The <var>callback</var> function used for 
   * traversing the current array.
   *
   * @param {Object} [thisObject] The object which will be the <var>this</var> 
   * within the scope of the <var>callback</var> function, for each invocation.
   *
   * @throws {TypeError} If <var>callback</var> is not a function.
   *
   * @returns {Boolean} False is returned if the <var>callback</var> returns 
   * false once. Otherwise, this method returns true.
   *
   * @see $.js.Object.every
   */
  every : function (callback, thisObject) {
    if (typeof callback != 'function') {
      throw new TypeError('The first argument must be a function.');
    }

    for (var i = 0, n = this.length; i < n; i++) {
      if ((i in this) && !callback.call(thisObject, this[i], i, this)) {
        return false;
      }
    }

    return true;
  },

  /**
   * Check if the <var>callback</var> function returns true for at least one 
   * element in the current array.
   *
   * <p>The <var>callback</var> function is invoked for each element in the 
   * current array, and it is given three arguments: the value, the index, and 
   * the entire array being iterated over.
   *
   * <p>Iteration over the array stops once reaching the end, or once the 
   * <var>callback</var> function returns true.
   *
   * <p>This method does not alter the current array.
   *
   * @example
   * <code>var <var>arr</var> = [10, 11, 12];</code>
   *
   * // The following returns true.
   * <code><var>arr</var>.some(function (<var>value</var>) {
   *   return <var>value</var> &gt; 11;
   * });</code>
   *
   * @param {Function} callback The <var>callback</var> function used for 
   * traversing the current array.
   *
   * @param {Object} [thisObject] The object which will be the <var>this</var> 
   * within the scope of the <var>callback</var> function, for each invocation.
   *
   * @throws {TypeError} If <var>callback</var> is not a function.
   *
   * @returns {Boolean} True is returned if the <var>callback</var> returns true 
   * once. Otherwise, this method returns false.
   *
   * @see $.js.Object.some
   */
  some : function (callback, thisObject) {
    if (typeof callback != 'function') {
      throw new TypeError('The first argument must be a function.');
    }

    for (var i = 0, n = this.length; i < n; i++) {
      if ((i in this) && callback.call(thisObject, this[i], i, this)) {
        return true;
      }
    }

    return false;
  },

  /**
   * Create a new array with the same length as the current array using the 
   * values returned by the <var>callback</var> function.
   *
   * <p>The <var>callback</var> function is invoked for each element in the 
   * current array, and it is given three arguments: the value, the index, and 
   * the entire array being iterated over.
   *
   * <p>This method does not alter the current array.
   *
   * @example
   * <code>var <var>arr1</var> = [5, 11, 7];</code>
   *
   * // Let's double the numbers:
   * <code>var <var>arr2</var> = <var>arr1</var>.map(function (<var>value</var>) {
   *   return <var>value</var> * 2;
   * });</code>
   *
   * // Now <code><var>arr2</var> = [10, 22, 14]</code>.
   *
   * @param {Function} callback The <var>callback</var> function used for 
   * traversing the current array.
   *
   * @param {Object} [thisObject] The object which will be the <var>this</var> 
   * within the scope of the <var>callback</var> function, for each invocation.
   *
   * @throws {TypeError} If <var>callback</var> is not a function.
   *
   * @returns {Array} The new array has the same length as the current array, 
   * but the values are those returned by the <var>callback</var> function.
   *
   * @see $.js.Object.map
   */
  map : function (callback, thisObject) {
    if (typeof callback != 'function') {
      throw new TypeError('The first argument must be a function.');
    }

    var n = this.length;
    var resArr = new Array(n);

    for (var i = 0; i < n; i++) {
      if (i in this) {
        resArr[i] = callback.call(thisObject, this[i], i, this);
      }
    }

    return resArr;
  },

  /**
   * Apply the <var>callback</var> function to two values from the current 
   * array, from left to right, simultaneously, for the purpose of reducing the 
   * array to a single value.
   *
   * <p>The <var>callback</var> function is called for each element in the 
   * current array, and it is given four arguments: the previous value, the 
   * current value, the current index, and the entire array being iterated over.
   *
   * <p>This method does not alter the current array.
   *
   * <p>Iteration starts from the first element in the current array (index 0) 
   * going up to the last element (<code>array.length-1</code>), one by one.
   *
   * @example
   * <code>var <var>arr</var> = [5, 10, 6];</code>
   *
   * // Let's calculate the sum:
   * <code>var <var>result</var> = <var>arr</var>.reduce(
   *   function (<var>sum</var>, <var>value</var>) {
   *     return <var>sum</var> + <var>value</var>;
   *   },
   * 0);</code>
   *
   * // Now <var>result</var> = 21.
   *
   * @param {Function} callback The <var>callback</var> function used for 
   * traversing the current array.
   *
   * @param [initialValue] The initial value used when the <var>callback</var> 
   * function is first invoked. If the initial value is not provided, then the 
   * method uses the value of the first element found in the array being 
   * iterated.
   *
   * @throws {TypeError} If <var>callback</var> is not a function.
   *
   * @returns The result of the last <var>callback</var> function invocation.
   *
   * @see $.js.Array.reduceRight
   * @see $.js.Object.reduce
   */
  reduce : function (callback, initialValue) {
    if (typeof callback != 'function') {
      throw new TypeError('The first argument must be a function.');
    }

    var res, found_initial = false, n = this.length;

    if (typeof initialValue != 'undefined') {
      found_initial = true;
      res = initialValue;
    } else if (n == 0) {
      throw new TypeError();
    }

    for (var i = 0; i < n; i++) {
      if (!(i in this)) {
        continue;
      }

      if (!found_initial) {
        found_initial = true;
        res = this[i];
        continue;
      }

      res = callback.call(null, res, this[i], i, this);
    }

    if (!found_initial) {
      throw new TypeError();
    }

    return res;
  },

  /**
   * Apply the <var>callback</var> function to two values from the current 
   * array, from right to left, simultaneously, for the purpose of reducing the 
   * array to a single value.
   *
   * <p>The <var>callback</var> function is called for each element in the 
   * current array, and it is given four arguments: the previous value, the 
   * current value, the current index, and the entire array being iterated over.
   *
   * <p>This method does not alter the current array.
   *
   * <p>Iteration starts from the last element in the current array 
   * (<code>array.length-1</code>) going down to 0, the first element, one by 
   * one.
   *
   * @example
   * <code>var arr = [5, 10, 6];</code>
   *
   * // Let's calculate the sum:
   * <code>var result = arr.reduce(function (<var>sum</var>, <var>value</var>) {
   *   return <var>sum</var> + <var>value</var>;
   * }, 0);</code>
   *
   * // Now result = 21.
   *
   * @param {Function} callback The callback function used for traversing the 
   * current array.
   *
   * @param [initialValue] The initial value used when the <var>callback</var> 
   * function is first invoked. If the initial value is not provided, then the 
   * method uses the value of the last element found in the array being 
   * iterated.
   *
   * @throws {TypeError} If <var>callback</var> is not a function.
   *
   * @returns The result of the last <var>callback</var> function invocation.
   *
   * @see $.js.Array.reduce
   * @see $.js.Object.reduce
   */
  reduceRight : function (callback, initialValue) {
    if (typeof callback != 'function') {
      throw new TypeError('The first argument must be a function.');
    }

    var res, found_initial = false, i = this.length - 1;

    if (typeof initialValue != 'undefined') {
      found_initial = true;
      res = initialValue;
    } else if (i == -1) {
      return null;
    }

    for ( ; i >= 0; i--) {
      if (!(i in this)) {
        continue;
      }

      if (!found_initial) {
        found_initial = true;
        res = this[i];
        continue;
      }

      res = callback.call(null, res, this[i], i, this);
    }

    if (!found_initial) {
      throw new TypeError();
    }

    return res;
  },

  /**
   * Return the first index where the given value is found in the current array.
   *
   * <p>Note that the method uses strict comparison for finding the value 
   * (<code>===</code>).
   *
   * @example
   * <code>var arr = ['a', 'b', 'c', 'd', 'b'];
   * arr.indexOf('b');</code>
   * // Returns 1.
   *
   * <code>arr.indexOf('b', 2);</code>
   * // Returns 4.
   *
   * <code>arr.indexOf('b', -2);</code>
   * // Returns 4.
   *
   * @param value The value you want to find in the current array.
   *
   * @param {Number} [offset=0] Zero-based index from where to start searching 
   * the array.
   * 
   * <p>If the argument is not given, the entire array is searched.
   * 
   * <p>If the number is negative, then the <var>offset</var> is considered to 
   * be an offset from the end of the array. Thus, given a negative index like 
   * -3, it would mean that only the last three elements are checked.
   *
   * <p>The method always starts searching from the calculated/given 
   * <var>offset</var> increasing one by one up to the array length (forward 
   * search).
   *
   * @throws {TypeError} If <var>value</var> is undefined.
   *
   * @returns {Number} The element index where the first occurrence of 
   * <var>value</var> was found in the current array. If the value was not 
   * found, -1 is returned.
   *
   * @see $.js.Array.lastIndexOf
   */
  indexOf : function (value, offset) {
    var n = this.length;

    if (n == 0) {
      return -1;
    } else if (typeof value == 'undefined') {
      throw new TypeError('The first argument is undefined.');
    }

    if (isNaN(offset)) {
      offset = 0;
    } else {
      offset = offset < 0 ? Math.ceil(offset) : Math.floor(offset);
      if (offset < 0) {
        offset += n;
      }
    }

    for ( ; offset < n; offset++) {
      if ((offset in this) && this[offset] === value) {
        return offset;
      }
    }

    return -1;
  },

  /**
   * Return the last index where the given value is found in the current array.
   *
   * <p>Note that the method uses strict comparison for finding the value 
   * (<code>===</code>).
   *
   * @example
   * <code>var <var>arr</var> = ['a', 'b', 'c', 'd', 'b'];
   * <var>arr</var>.lastIndexOf('b');</code>
   * // Returns 4.
   *
   * <code><var>arr</var>.lastIndexOf('b', 2);</code>
   * // Returns 1.
   *
   * <code><var>arr</var>.lastIndexOf('b', -2);</code>
   * // Returns 1.
   *
   * @param value The value you want to find in the current array.
   *
   * @param {Number} [offset=this.length] Zero-based index from where to start 
   * searching backwards.
   * 
   * <p>If the argument is not given, the entire array is searched.
   * 
   * <p>If the number is negative, then the <var>offset</var> is considered to 
   * be an offset from the end of the array. Thus, given a negative index like 
   * -3, it would mean that the last <strong>two</strong> elements are 
   *  <strong>not</strong> checked.
   *
   * <p>The method always starts searching from the calculated/given 
   * <var>offset</var> decreasing one by one, down to index 0 (backwards 
   * search).
   *
   * @throws {TypeError} If <var>value</var> is undefined.
   *
   * @returns {Number} The element index where the last occurrence of 
   * <var>value</var> was found in the current array. If the value was not 
   * found, -1 is returned.
   *
   * @see $.js.Array.indexOf
   */
  lastIndexOf : function (value, offset) {
    var n = this.length;

    if (n == 0) {
      return -1;
    } else if (typeof value == 'undefined') {
      throw new TypeError('The first argument is undefined.');
    }

    if (isNaN(offset)) {
      offset = n - 1;
    } else {
      offset = offset < 0 ? Math.ceil(offset) : Math.floor(offset);
      if (offset < 0) {
        offset += n;
      } else if (offset >= n) {
        offset = n - 1;
      }
    }

    for ( ; offset >= 0; offset--) {
      if ((offset in this) && this[offset] === value) {
        return offset;
      }
    }

    return -1;
  }
};


/**
 * Holds the methods used for manipulating strings. Some of them are already 
 * available in JavaScript 1.6, natively.
 *
 * <p>By default, the global <var>String.prototype</var> object is extended to 
 * contain all the methods in this namespace, if they do not exists already.  
 * Thus, native String methods are not overwritten.
 *
 * <p>All of these methods use their <var>this</var> object. As such, you need 
 * to pass the correct <var>this</var> object.
 *
 * <p>In the examples provided for each method we will assume that the strings 
 * defined already have the methods. Thus code like 
 * <code><var>string</var>.trim()</code> can be written directly.
 *
 * @namespace Holds the methods used for manipulating strings. Some of them are 
 * already available in JavaScript 1.6, natively.
 */
$.js.String = {
  /**
   * Trim whitespace from the start and the end of the string.
   *
   * @example
   * <code>var str = " \n test \n  ";
   * var str2 = str.trim();</code>
   * // Now <code>str2 = 'test'</code>.
   *
   * @returns {String} The string without any whitespace at the beginning, nor 
   * at the end.
   *
   * @see $.js.String.trimLeft
   * @see $.js.String.trimRight
   */
  trim : function () {
    return this.replace(/^\s+|\s+$/g, '');
  },

  /**
   * Trim whitespace from the start of the string.
   *
   * @example
   * <code>var str = " \n test \n  ";
   * var str2 = str.trimLeft();</code>
   * // Now <code>str2 = "test \n  "</code>.
   *
   * @returns {String} The string without any whitespace at the beginning.
   *
   * @see $.js.String.trim
   * @see $.js.String.trimRight
   */
  trimLeft : function () {
    return this.replace(/^\s+/, '');
  },

  /**
   * Trim whitespace from the end of the string.
   *
   * @example
   * <code>var str = " \n test \n  ";
   * var str2 = str.trimRight();</code>
   * // Now <code>str2 = " \n test"</code>.
   *
   * @returns {String} The string without any whitespace at the end.
   *
   * @see $.js.String.trim
   * @see $.js.String.trimLeft
   */
  trimRight : function () {
    return this.replace(/\s+$/, '');
  },

  /**
   * Convert significant characters into entities. Any XML/HTML code has 
   * significant characters, like angle brackets and quotes. This method 
   * converts them to HTML entities, such that the resulting string can be used 
   * in Web pages without any problems.
   *
   * <p>The characters being encoded are:
   *
   * <ul>
   *   <li>' &amp; ' to ' &amp;amp; '
   *   <li>' &lt; ' to ' &amp;lt; '
   *   <li>' &gt; ' to ' &amp;gt; '
   *   <li>' " ' to ' &amp;#34; '
   *   <li>" ' " to ' &amp;#39; '
   * </ul>
   *
   * @returns {String} The string with the special characters encoded.
   *
   * @see $.js.String.unescapeXML
   */
  escapeXML : function () {
    return this.replace(/&/g,  '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g,  '&gt;')
      .replace(/"/g, '&#34;')
      .replace(/'/g, '&#39;');
  },

  /**
   * Decode significant characters from entities. This is the reverse method of 
   * {@link $.js.String.escapeXML}.
   *
   * @returns {String} The string with the special characters decoded.
   *
   * @see $.js.String.escapeXML
   */
  unescapeXML : function () {
    return this.replace(/&lt;/g, '<')
      .replace(/&gt;/g,  '>')
      .replace(/&#34;/g, '"')
      .replace(/&#39;/g, "'")
      .replace(/&amp;/g, '&');
  },

  /**
   * Strip tags from a string.
   *
   * <p>Optionally, you may provide the list of tags to be stripped from the 
   * string. Each argument is considered a tag name.
   *
   * <p>If no argument is provided, then all the tags in the string are 
   * stripped.
   *
   * @params {String} [tag1] The name of the tag you want to strip.
   * @params {String} [tagN] The name of the tag you want to strip.
   *
   * @returns {String} The string with tags stripped.
   */
  stripTags : function () {
    var tags = '[a-zA-Z][a-zA-Z\d_]*';
    if (arguments.length > 0) {
      tags = '';
      $.js.Array.forEach.call(arguments, function (val) {
        tags += '|' + $.js.String.escapeRegexp.call(val);
      });
      tags = '(' + tags.substr(1) + ')';
    }

    var regex = new RegExp('</?' + tags + '[^>]*>', 'g');

    return this.replace(regex, '');
  },

  /**
   * Escape special characters in regular expressions.
   *
   * @returns {String} The string with the special characters escaped.
   */
  escapeRegexp : function () {
    return this.replace(/([$^\/()\\|?+*\[\]{}.-])/g, "\\$1");
  }
};

if (extend_objects) {
  Object.extend = $.extend;
  $.extend(Object, $.js.Object);
  $.extend(Array.prototype, $.js.Array);
  $.extend(String.prototype, $.js.String);
}

})();
